﻿/**
 * 
 * Copyright 2009, 2010 Mike Jamer [email: mikejamer@gmail.com ]
 * 
 * This File is part of Moose IRC
 * 
 * MooseIRC is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * MooseIRC is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with MooseIRC.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using moose.irc;

namespace MooseIRC.Client.Server {
    

    /// <summary>
    /// Base Client Class
    /// </summary>
    internal abstract class Client : object, IDisposable {

        #region [Local Attributes]

        /// <summary>
        /// Client ID
        /// </summary>
        private Guid m_client_id;

        /// <summary>
        /// Client Socket
        /// </summary>
        protected Socket m_socket;

        /// <summary>
        /// Recieve Buffer
        /// </summary>
        protected StringBuilder m_receive_buffer;

        #endregion

        #region [Public Properties]

        /// <summary>
        /// Gets Client ID
        /// </summary>
        public Guid ClientID {
            get {
                return m_client_id;
            }
        }

        #endregion

        #region [Construction]

        /// <summary>
        /// Initialize Client
        /// </summary>
        /// <param name="socket"></param>
        public Client(Socket socket) : base() {
            m_client_id = Guid.NewGuid();
            m_receive_buffer = new StringBuilder();
            m_socket = socket;

            recieve();

            return;
        }

        #endregion

        #region [Async. Socket Callbacks]

        /// <summary>
        /// Fires when we recieve data from the status client
        /// </summary>
        /// <param name="result">Async. Result</param>
        protected void onRecieveComplete(IAsyncResult result) {
            byte[] buffer = (byte[])result.AsyncState;
            int recv = 0;
            try
            {
                recv = m_socket.EndReceive(result);
            }
            catch (Exception)
            {
                //Application has been closed, well thats the most likely cause, and the only cause that I've seen so far...
                return;
            }
            string message = Settings.DEFAULT_ENCODING.GetString(buffer, 0, recv);
            
            m_receive_buffer.Append(message);
            foreach (string response in processRecieveBuffer()) {
                dispatch_client(response);
            }

            recieve();
            return;
        }

        /// <summary>
        /// Fires when transmission is complete to status client
        /// </summary>
        /// <param name="result">Async. Result</param>
        protected void onSendComplete(IAsyncResult result) {
            byte[] buffer = (byte[])result.AsyncState;
            int sent = m_socket.EndSend(result);
            if (buffer.Length != sent) {
                Console.Error.WriteLine("Transmission to client failed");
            }
            return;
        }

        #endregion

        #region [Public Methods]

        /// <summary>
        /// Transmit a new message to client
        /// </summary>
        /// <param name="message">Message to transmit</param>
        public void send(string message) {
            byte[] buffer = Settings.DEFAULT_ENCODING.GetBytes(message + "\r\n");
            try
            {
                m_socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, onSendComplete, buffer);
            }
            catch (Exception)
            {
                return;
            }
            return;
        }

        #endregion

        #region [Helper Methods]

        /// <summary>
        /// Kick off Async. Recieve
        /// </summary>
        private void recieve() {
            byte[] buffer = new byte[512];
            try
            {
                m_socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, onRecieveComplete, buffer);
            }
            catch (Exception)
            {
                return;
            }
            return;
        }

        /// <summary>
        /// Processes Recieve Buffer
        /// </summary>
        /// <returns>Array of all messages that have been completely recieved</returns>
        private string[] processRecieveBuffer() {
            List<string> responses = new List<string>();
            while (m_receive_buffer.ToString().IndexOf("\r\n") != -1) {
                string buf = m_receive_buffer.ToString();
                responses.Add(buf.Substring(0, buf.IndexOf("\r\n")));
                m_receive_buffer.Remove(0,
                    buf.Substring(0, buf.IndexOf("\r\n")
                    ).Length + 2); //Remove message, plus the CRLF pair
            }
            return responses.ToArray();
        }

        /// <summary>
        /// Dispatch action based on response
        /// </summary>
        /// <param name="response"></param>
        protected abstract void dispatch_client(string response);

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Dispose Implementation
        /// </summary>
        public void Dispose() {

            if (m_socket != null) {
                m_socket.Close(5000);
                m_socket = null;
            }
            return;
        }

        #endregion
    }
}
